package com.db;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Date;
import java.util.Iterator;
import java.util.Properties;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class AsyncFileAppender implements Appender {
	private static final AsyncFileAppender appender = new AsyncFileAppender();
	private String propertyFile = "/MyLoggerProperties.properties";
	private String format;
	private PrintWriter write;
	private int capacity;
	private Thread writingThread = new Thread() {
	    @Override
		public void run() {
			synchronized(appender) {
			    try {
				    while (true) {
					    while (!(queue.isEmpty())) {
						    String message = queue.take();
						    write.print(message);
						    write.flush();
					    }
                        appender.wait();
		    	    }
			    } catch(InterruptedException e) {
				    Thread.currentThread().interrupt();
			    }
			}
		};
	};
	private BlockingQueue<String> queue = new LinkedBlockingQueue<String>(1);
	private AsyncFileAppender() {
		this.format = "%d %m [%t] \n";
		this.capacity = 1;
		try {
			InputStream in = this.getClass().getResourceAsStream(propertyFile);
			Properties prop = new Properties();
			prop.load(in);
			String nameFile = prop.getProperty("myLogger.appender.nameFile");
			if (nameFile == null) {
				write = new PrintWriter(new BufferedWriter(new FileWriter("log.txt", true)), true);
			} 
			else {
				write = new PrintWriter(new BufferedWriter(new FileWriter(nameFile, true)), true);
			}
			this.writingThread.start();
		} catch (Exception e) {
			e.printStackTrace();
		}
		Runtime.getRuntime().addShutdownHook(new Thread() {
			@Override
			public void run() {
				while (!(queue.isEmpty())) {
					try {
						Thread.sleep(5);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				writingThread.interrupt();
			}
		});
	}
	public static AsyncFileAppender getAppender() {
		return appender;
	}
	public void append(String message) {
		synchronized(appender) {
		    String result = format.replaceAll("%d", new Date().toString()).replaceAll("%m", message).replaceAll("%t", Thread.currentThread().getName());
			    if (queue.size() < capacity) {
				    queue.add(result);
			    } else {
				    BlockingQueue<String> queueHelp = new LinkedBlockingQueue<String>(capacity*2);
				    this.capacity = this.capacity * 2;
				    Iterator<String> iter = queue.iterator();
				    while(iter.hasNext())
					    queueHelp.add(iter.next());
				    queue = queueHelp;
				    queue.add(result);
			    }
			    appender.notifyAll();
		  }
	 }
}
